import requests
import time
import json
from datetime import datetime
import random
import os
from DB_operations import BilibiliDB

class BZhan:
    """
    一个爬取B站热门视频数据的类。
    """
    def __init__(self):
        """
        初始化请求头、API URL和结果存储列表。
        """
        self.headers = {
            "Origin": "https://www.bilibili.com",
            "Pragma": "no-cache",
            "Referer": "https://www.bilibili.com/v/popular/all/?spm_id_from=333.1007.0.0",
            "Sec-Fetch-Dest": "empty",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Site": "same-site",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0",
        }
        self.url = "https://api.bilibili.com/x/web-interface/popular"
        self.results = []

    def get_data(self, page):
        """
        获取指定页数的热门视频数据。

        参数:
        page (int): 页数

        返回:
        list: 视频数据列表，请求失败或解析错误时返回None
        """
        try:
            params = {
                "ps": "20",
                "pn": str(page),
            }
            response = requests.get(self.url, headers=self.headers, params=params)
            response.raise_for_status()  # 检查请求是否成功
            return response.json()['data']['list']
        except requests.exceptions.RequestException as e:
            print(f"请求出错: {e}")
            return None
        except KeyError as e:
            print(f"数据解析出错: {e}")
            return None

    def parse_data(self, res):
        """
        解析视频数据并存储到结果列表中。

        参数:
        res (list): 视频数据列表
        """
        if not res:
            return

        for data in res:
            try:
                item = {
                    '标题': data['title'],
                    '播放量': data['stat']['view'],
                    '弹幕数': data['stat']['danmaku'],
                    '点赞数': data['stat']['like'],
                    '投币数': data['stat']['coin'],
                    '收藏数': data['stat']['favorite'],
                    '分享数': data['stat']['share'],
                    'UP主': data['owner']['name'],
                    'UP主ID': data['owner']['mid'],
                    '视频分区': data['tname'],
                    '视频简介': data['desc'],
                    '视频时长': self.format_duration(data['duration']),
                    '发布时间': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data['pubdate'])),
                    '视频链接': f"https://www.bilibili.com/video/{data['bvid']}"
                }
                self.results.append(item)
                print(f"成功爬取: {item['标题']} - UP主: {item['UP主']}")
            except KeyError as e:
                print(f"解析数据项出错: {e}")
                continue

    def format_duration(self, seconds):
        """
        将秒数转换为时分秒格式。

        参数:
        seconds (int): 秒数

        返回:
        str: 格式化后的时长字符串
        """
        m, s = divmod(seconds, 60)
        h, m = divmod(m, 60)
        if h > 0:
            return f'{h:02d}:{m:02d}:{s:02d}'
        else:
            return f'{m:02d}:{s:02d}'

    def save_data(self):
        """
        保存数据到JSON文件至templates文件夹下。
        """
        root_dir = os.path.dirname(os.path.abspath(__file__))
        templates_dir = os.path.join(root_dir, 'templates')
        if not os.path.exists(templates_dir):
            os.makedirs(templates_dir)
        filename = f"bilibili_popular_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
        file_path = os.path.join(templates_dir, filename)

        # 保存数据到JSON文件
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(self.results, f, ensure_ascii=False, indent=2)
        print(f"\n数据已保存到文件: {file_path}")

    def save_to_database(self):
        """保存数据到数据库"""
        db = None  # 初始化 db 变量
        try:
            db = BilibiliDB()  # 创建数据库连接
            db.create_bilibili_videos_table()  # 创建表
            db.truncate_table()  # 清空表
            
            db.save_to_db(self.results)  # 保存数据到数据库
            
        except Exception as e:
            print(f"保存到数据库时出错: {e}")
        finally:
            if db:  # 确保 db 被创建后再调用 close
                db.close()

    def main(self):  # 移除max_pages参数
        """
        爬虫主函数，负责调用其他函数完成数据爬取和保存。
        让用户输入要爬取的页数。
        """
        while True:
            try:
                max_pages = input("\n请输入要爬取的页数（每页20个视频）: ")
                max_pages = int(max_pages)
                if max_pages <= 0:
                    print("页数必须大于0，请重新输入")
                    continue
                break
            except ValueError:
                print("请输入有效的数字")
        
        print(f"\n开始爬取B站热门视频数据，计划爬取{max_pages}页（共{max_pages * 20}个视频）...")

        for i in range(1, max_pages + 1):
            print(f"\n正在爬取第 {i}/{max_pages} 页...")
            res = self.get_data(i)
            if res:
                self.parse_data(res)
                # 随机延时2-5秒
                time.sleep(random.uniform(2, 5))
            else:
                print(f"第 {i} 页数据获取失败，跳过...")
                continue

        print(f"\n爬取完成，共获取 {len(self.results)} 个视频数据")
        self.save_data()  # 保存到JSON
        self.save_to_database()  # 保存到数据库

if __name__ == '__main__':
    bz = BZhan()
    bz.main()  # 移除max_pages参数
